Introduction to scRNAseq and data pre-processing

link to powerpoint slides: A power point presentation will describe the general principles of single-cell ‘omics’ and basics about the platforms (~15 - 20 minutes)


1. FASTQ Files, genome alignments, and deconvolution of cells

This workshop provides an overview of key quality control (QC) metrics and processing steps in the initial steps of single-cell transcriptomic data analysis. While detailed tutorials for processing FASTQ files, performing genome alignments, and deconvoluting cells are available through various resources, we’ll focus on understanding the fundamental QC metrics and data structures.

Links to alignment preliminary analysis tutorials:

  • Vendor-specific pipeline resources (e.g., 10X Genomics)

  • Galaxy

  • Google colab hands-on experience (under construction)

1.1. Understanding the ‘knee plot’

Upon receiving aligned single-cell/nuclei sequencing data, one of the initial plots to examine is the ‘knee plot,’ commonly generated during analysis pipelines. This plot helps distinguish between barcodes representing cells and those representing background noise or empty Gel Emulsion beads (GEMs).

Gel Emulsion beads (GEMs) are microfluidic droplets used in platforms like 10X Genomics, each potentially containing a cell or nuclei. Distinguishing between GEMs with cells and empty GEMs is crucial for accurate analysis.

The knee plot visually depicts how a threshold is set to differentiate between cell barcodes and background noise. In platforms like 10X Genomics, cells within GEMs produce a larger number of unique mRNA molecules (UMIs) compared to empty GEMs. The knee plot typically shows a sudden drop (knee) in UMIs, indicating the separation between GEMs containing cells and those lacking them.

Figure from Danielski, K. (2023). Guidance on Processing the 10x Genomics Single Cell Gene Expression Assay. In: Calogero, R.A., Benes, V. (eds) Single Cell Transcriptomics. Methods in Molecular Biology, vol 2584. Humana, New York, NY. https://doi.org/10.1007/978-1-0716-2756-3_1

Interpreting the knee plot involves identifying this knee point. A distinct knee suggests successful separation, while the absence of a clear knee could indicate issues such as low RNA integrity or high ambient RNA contamination.

Figure from https://isoseq.how/umi/cell-calling.html


2. Setting up our R environment for data exploration

In preparation for the following hands-on experiences we will set up an R environment which has everything we need.

2.1. Load libraries

suppressPackageStartupMessages({
  library(Seurat)
  library(ggplot2)
  library(tidyr)
  library(scDblFinder)
  library(SingleCellExperiment)
})

2.2. Import data

For this tutorial we will use raw unprocessed data from human peripheral blood mononuclear cells (PBMCs) generated using the 10X Genomics platform which can be download from the Seurat page. These data should be downloaded, extracted, and placed in the downloads folder for this workshop.

# Load the PBMC dataset
pbmc.data <- Read10X(data.dir = "./downloads/filtered_gene_bc_matrices/hg19/")
# Initialize the Seurat object with the raw (non-normalized data).
seurat_object <- CreateSeuratObject(counts = pbmc.data, project = "pbmc3k", 
                                    min.cells = 3, min.features = 200)
Warning: Feature names cannot have underscores ('_'), replacing with dashes ('-')

Note: We apply some preliminary filters as we know that GEMs which very low numbers of genes or genes which are barely detected are unlikely to be actual cells.

2.3. Check that everything was loaded correctly

print(seurat_object)
An object of class Seurat 
13714 features across 2700 samples within 1 assay 
Active assay: RNA (13714 features, 0 variable features)
 1 layer present: counts

3. Overview of Single-cell Data Structure

To better understand how to work with single-cell/nuclei RNA sequencing data we will take a deep dive into how datasets are typically structured. In the diagram below we illustrate how droplet-based scRNAseq approaches separate cells and their mRNAs. But how does this translate into data?

Generated using biorender.com
Generated using biorender.com

3.1. The Gene x Cell Matrix

# Raw counts in a seurat object is saved in the assays-RNA-counts slot 
raw_counts = as.matrix(seurat_object@assays$RNA@layers$counts)
rownames(raw_counts) = rownames(seurat_object)
colnames(raw_counts) = colnames(seurat_object)

# Lets look at the first 4 columns and rows
print(raw_counts[1:4,1:4])
              AAACATACAACCAC-1 AAACATTGAGCTAC-1 AAACATTGATCAGC-1 AAACCGTGCTTCCG-1
AL627309.1                   0                0                0                0
AP006222.2                   0                0                0                0
RP11-206L10.2                0                0                0                0
RP11-206L10.9                0                0                0                0

What does this mean?

Columns: The columns represent a unique barcode. As we can see below, this barcode marks an individual cell which is how we can identify each one. So in this object, the columns are the cells

Generated in biorender.com and adapted from Danielski, K. (2023). Guidance on Processing the 10x Genomics Single Cell Gene Expression Assay. In: Calogero, R.A., Benes, V. (eds) Single Cell Transcriptomics. Methods in Molecular Biology, vol 2584. Humana, New York, NY. https://doi.org/10.1007/978-1-0716-2756-3_1
Generated in biorender.com and adapted from Danielski, K. (2023). Guidance on Processing the 10x Genomics Single Cell Gene Expression Assay. In: Calogero, R.A., Benes, V. (eds) Single Cell Transcriptomics. Methods in Molecular Biology, vol 2584. Humana, New York, NY. https://doi.org/10.1007/978-1-0716-2756-3_1

Rows: We see that the first few rows have names such as RP11-206L10.9. A quick google search will tell you that this is a human gene (Ensembl ID - ENSG00000237491). So we see in this object that the rows represent the genes.

3.2. Counting the number of cells and genes

Single-cell transcriptomic analysis tools (e.g., Seurat) does a lot of the heavy lifting for us when it comes to counts and other summary data information. For example, the following code will tell you the number of genes and cells, as well store raw, normalized, and scaled data, dimension reductions, and other analyses which may have been performed.

print(seurat_object)
An object of class Seurat 
13714 features across 2700 samples within 1 assay 
Active assay: RNA (13714 features, 0 variable features)
 1 layer present: counts

This indicates that we have 13,714 genes (features) across 2,700 samples.

However, we can also extract that information from the count table:

print(paste0('Cells: ', ncol(raw_counts)))
[1] "Cells: 2700"
print(paste0('Features:', nrow(raw_counts)))
[1] "Features:13714"

These numbers should match!

3.3. A deeper dive into counts

3.3a. What is sequencing depth?

# Calculate the total reads in each individual cell
read_depths = colSums(raw_counts)

# How are they distributed?
hist(read_depths, main = "Histogram of read depth", 
     xlab = "Depth", ylab = "Frequency",
     breaks = 200)

This histogram shows that the number of mRNA transcripts detected in each cell ranged from 546 to 15,818, but that most were around 2,000. So what might account for these differences?

  • Cell types: Some cell types may simply just express more mRNA than others. For example, Hepatocytes are known to contain a lot of RNA while others may not.

  • Stochastic gene expression: Fluctuations in transcription and degradation from normal biological processes, cell states, and other factors in individual cells which have very low amounts of RNA to begin can appear as big differences.

  • Technical variability: Sequencing depth, assay kits, and other technical factors may contribute to these differences.

The zero-inflation question: In the early days of scRNAseq it was a widely accepted that data was zero-inflated, i.e., it had more zero’s than it should because of technical challenges. Today, we better understand that the large number of zero’s are not only technical, but in fact does reflect true biology as well. Here are some interesting papers on the topic: Jiang et al., 2022


4. Assessing data quality

Any analysis, no matter of size, should undergo rigorous QC. When it comes to single-cell transcriptomic data, there are several computational tools that help you in the process from enabling visualizations to performing a panel of QC tests and reporting several different metrics for the end user to evaluate. A list of just some of these tools can be found here.

https://www.scrna-tools.org/
https://www.sc-best-practices.org/preprocessing_visualization/quality_control.html

Let’s go through some of the most common QC metrics, what to they mean, and why do they matter?

4.1. Genes and transcripts

How does the number of detected genes and transcripts indicate quality? While we don’t have a known number of expressed genes for each cell type, we do know that cells require the expression of hundreds to thousands of genes to function properly.

Number of genes (features)

We can check these values by running the code below:

VlnPlot(seurat_object, features = 'nFeature_RNA') + NoLegend()
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

Here we can see that (1) the number of genes detected ranges from ~200 - 3000 and (2) most express ~900 genes. Is this normal and expected? In reality, there is no universal threshold, these values will depend on the cell type and the sequencing depth. But we do know that if most cells had extemely low counts that either cells were not healthy/intact or sequencing depth was much too low. It could also point to errors further upstream such as alignment to the incorrect reference genome.

Number of mRNA molecules (count)

We can also examine the total number of mRNA molecules detected:

VlnPlot(seurat_object, features = 'nCount_RNA')
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

As above, we can see a good distribution centered around 2500 mRNA molecules. These value should always be higher than the number of features.

Correlation of features and molecules

A third way to look at this data is to examine the correlation of features and mRNA molecules:

FeatureScatter(object = seurat_object, feature1 = 'nCount_RNA', feature2 = 'nFeature_RNA')

This metric is more informative because it tells you that as you detect more mRNA molecules, you also detect more different genes. This would generally be expected as you are getting a larger sampling of the mRNA resulting in an increased chance of sampling different genes. However, when you assess these plots, you will want to keep the biology in mind.

4.2. Mitochondrial RNA

Here, biological knowledge is extremely important for evaluating this metric and removing cells based on a threshold. Consider these three experiments:

  • Nuclei isolated from muscle cells which require a lot of energy

  • Muscle whole cells which require a lot of energy

  • Skin cells which are less metabolically active

What would you expect to see (qualitative) in the amount of mitochondrial genes for each of these experiments?

Assessing percent mitochondria counts

We can assess the percentage of mitochondrial genes by calculating the number of counts coming from mitochondrial genes (usually prefixed with mt- or MT-) compared to the total counts. This can be visualized like this:

# Calcuate the percentage of mitochondrial counts
seurat_object[["percent.mt"]] <- PercentageFeatureSet(seurat_object, pattern = "^MT-")

# Plot the values
VlnPlot(seurat_object, features = 'percent.mt')
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

We see that the percentage of counts representing mitochondrial genes is mostly below 5% but as high as ~25%. To evaluate these results think back to the three experiments and consider what you would expect to see:

  • Nuclei isolated from muscle cells which require a lot of energy

    Despite being metabolically active, nuclei isolation is expected to wash out mitochondria. High contamination is indicative of high ambient RNA or mitochondria sticking to the the nuclei.

  • Muscle whole cells which require a lot of energy

    Given that muscle cells are highly metabolically active and are known to have among the most mitochondria among mammalian cells, these data may show higher percentage of mitochondrial genes than most datasets.

  • Skin cells which are less metabolically active

    Given that these are less metabolically active, they may be expected to show similar or lower percentage of mitochondrial genes.

Correlation of counts and mitochondrial counts

As with the features and counts, we can look at how these correlate:

FeatureScatter(seurat_object, feature1 = 'nCount_RNA', feature2 = 'percent.mt')

Interestingly, these are not correlated at all. Prior to filtering, these values will be anti-correlated as mitochondria often makes up the ambient RNA (release from dead or lysed cells) and will be more represented in the background (low count cells).

4.3. Ambient RNA contamination

As with any single-cell transcriptomic tool, there are dozens, if not more, options for removal of abient RNA. Generally, the concept for ambient RNA removal is the same - substract genes which are present in the empty GEMs/wells from those which are found to have cells. Other techniques such as combinatorial barcoding approaches have to make different assumptions.

SoupX is one example of a tool which cleans up the soup (floating ambient RNA) from cells. This tools uses the most raw format of data which is generate before a threshold is set on the knee plot discussed above. For 10X Genomics these are stored in the raw_feature_bc_matrix and filtered_feature_bc_matrix folders.

This tutorial will not go through the entire ambient RNA removal workflow. Excellent examples can be found here and here.

4.4 Doublets

Another common challenge with using single-cell transcriptomic platforms is one that can affect most single-cell based techniques including flow cytometry - doublets. These can be a result of cells sticking to each other or being too close to each other during the sorting, and the rate is typically a function of the total number of cells examined (more cells results in more doublets).

Doublets can consist of either 2 cells of the same type (homotypic) or different cell types (heterotypic). As you can probably imagine, distinguising homotypic doublets is more challenging, but are generally considered to be less of a concern.

DoubletFinder and scDblFinder are R based tool for the identification of doublets based on the general principle outlined above.

sce = as.SingleCellExperiment(seurat_object)
Warning: Layer ‘data’ is emptyWarning: Layer ‘scale.data’ is empty
sce <- scDblFinder(sce)
Creating ~5000 artificial doublets...
Dimensional reduction
Evaluating kNN...
Training model...
iter=0, 271 cells excluded from training.
iter=1, 227 cells excluded from training.
iter=2, 209 cells excluded from training.
Threshold found:0.518
109 (4%) doublets called
# Transfer the doublet information to the seurat object
seurat_object$scDblFinder.class = colData(sce)$scDblFinder.class
seurat_object$scDblFinder.score = colData(sce)$scDblFinder.score

4.5. Filtering data

seurat_object <- subset(seurat_object, subset = nFeature_RNA > 200 & nFeature_RNA < 2500 & percent.mt < 5 & scDblFinder.class == 'singlet')

print(seurat_object)
An object of class Seurat 
26286 features across 2529 samples within 2 assays 
Active assay: SCT (12572 features, 3000 variable features)
 3 layers present: counts, data, scale.data
 1 other assay present: RNA
 2 dimensional reductions calculated: pca, umap

5. Normalization

seurat_object = SCTransform(seurat_object, verbose = F)
seurat_object = RunPCA(seurat_object, verbose = F)
seurat_object = RunUMAP(seurat_object, dims = 1:30, verbose = F)
DimPlot(seurat_object, group.by='orig.ident')

6. Dimension reduction and clustering; 7. Annotation of cell types; 8. Trajectories and RNA velocity; 9. Inferring cell-cell communication; 10. Spatial transcriptomics
LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBzY1JOQXNlcSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgSW50cm9kdWN0aW9uIHRvIHNjUk5Bc2VxIGFuZCBkYXRhIHByZS1wcm9jZXNzaW5nDQoNCipsaW5rIHRvIHBvd2VycG9pbnQgc2xpZGVzOiogQSBwb3dlciBwb2ludCBwcmVzZW50YXRpb24gd2lsbCBkZXNjcmliZSB0aGUgZ2VuZXJhbCBwcmluY2lwbGVzIG9mIHNpbmdsZS1jZWxsICdvbWljcycgYW5kIGJhc2ljcyBhYm91dCB0aGUgcGxhdGZvcm1zIChcfjE1IC0gMjAgbWludXRlcykNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuIEZBU1RRIEZpbGVzLCBnZW5vbWUgYWxpZ25tZW50cywgYW5kIGRlY29udm9sdXRpb24gb2YgY2VsbHMNCg0KVGhpcyB3b3Jrc2hvcCBwcm92aWRlcyBhbiBvdmVydmlldyBvZiBrZXkgcXVhbGl0eSBjb250cm9sIChRQykgbWV0cmljcyBhbmQgcHJvY2Vzc2luZyBzdGVwcyBpbiB0aGUgaW5pdGlhbCBzdGVwcyBvZiBzaW5nbGUtY2VsbCB0cmFuc2NyaXB0b21pYyBkYXRhIGFuYWx5c2lzLiBXaGlsZSBkZXRhaWxlZCB0dXRvcmlhbHMgZm9yIHByb2Nlc3NpbmcgRkFTVFEgZmlsZXMsIHBlcmZvcm1pbmcgZ2Vub21lIGFsaWdubWVudHMsIGFuZCBkZWNvbnZvbHV0aW5nIGNlbGxzIGFyZSBhdmFpbGFibGUgdGhyb3VnaCB2YXJpb3VzIHJlc291cmNlcywgd2UnbGwgZm9jdXMgb24gdW5kZXJzdGFuZGluZyB0aGUgZnVuZGFtZW50YWwgUUMgbWV0cmljcyBhbmQgZGF0YSBzdHJ1Y3R1cmVzLg0KDQoqKipMaW5rcyB0byBhbGlnbm1lbnQgcHJlbGltaW5hcnkgYW5hbHlzaXMgdHV0b3JpYWxzOioqKg0KDQotICAgVmVuZG9yLXNwZWNpZmljIHBpcGVsaW5lIHJlc291cmNlcyAoZS5nLiwgWzEwWCBHZW5vbWljc10oaHR0cHM6Ly93d3cuMTB4Z2Vub21pY3MuY29tL3N1cHBvcnQvc29mdHdhcmUvY2VsbC1yYW5nZXIvbGF0ZXN0L3R1dG9yaWFscy9jci10dXRvcmlhbC1pbikpDQoNCi0gICBbR2FsYXh5XShodHRwczovL3RyYWluaW5nLmdhbGF4eXByb2plY3Qub3JnL3RyYWluaW5nLW1hdGVyaWFsL3RvcGljcy9zaW5nbGUtY2VsbC90dXRvcmlhbHMvc2NybmEtcHJlcHJvY2Vzc2luZy90dXRvcmlhbC5odG1sKQ0KDQotICAgR29vZ2xlIGNvbGFiIGhhbmRzLW9uIGV4cGVyaWVuY2UgKFsqdW5kZXIgY29uc3RydWN0aW8qbl17LnVuZGVybGluZX0pDQoNCiMjIyAxLjEuIFVuZGVyc3RhbmRpbmcgdGhlICdrbmVlIHBsb3QnDQoNClVwb24gcmVjZWl2aW5nIGFsaWduZWQgc2luZ2xlLWNlbGwvbnVjbGVpIHNlcXVlbmNpbmcgZGF0YSwgb25lIG9mIHRoZSBpbml0aWFsIHBsb3RzIHRvIGV4YW1pbmUgaXMgdGhlICdrbmVlIHBsb3QsJyBjb21tb25seSBnZW5lcmF0ZWQgZHVyaW5nIGFuYWx5c2lzIHBpcGVsaW5lcy4gVGhpcyBwbG90IGhlbHBzIGRpc3Rpbmd1aXNoIGJldHdlZW4gYmFyY29kZXMgcmVwcmVzZW50aW5nIGNlbGxzIGFuZCB0aG9zZSByZXByZXNlbnRpbmcgYmFja2dyb3VuZCBub2lzZSBvciBlbXB0eSBHZWwgRW11bHNpb24gYmVhZHMgKEdFTXMpLg0KDQoqR2VsIEVtdWxzaW9uIGJlYWRzIChHRU1zKSogYXJlIG1pY3JvZmx1aWRpYyBkcm9wbGV0cyB1c2VkIGluIHBsYXRmb3JtcyBsaWtlIDEwWCBHZW5vbWljcywgZWFjaCBwb3RlbnRpYWxseSBjb250YWluaW5nIGEgY2VsbCBvciBudWNsZWkuIERpc3Rpbmd1aXNoaW5nIGJldHdlZW4gR0VNcyB3aXRoIGNlbGxzIGFuZCBlbXB0eSBHRU1zIGlzIGNydWNpYWwgZm9yIGFjY3VyYXRlIGFuYWx5c2lzLg0KDQpUaGUga25lZSBwbG90IHZpc3VhbGx5IGRlcGljdHMgaG93IGEgdGhyZXNob2xkIGlzIHNldCB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gY2VsbCBiYXJjb2RlcyBhbmQgYmFja2dyb3VuZCBub2lzZS4gSW4gcGxhdGZvcm1zIGxpa2UgMTBYIEdlbm9taWNzLCBjZWxscyB3aXRoaW4gR0VNcyBwcm9kdWNlIGEgbGFyZ2VyIG51bWJlciBvZiB1bmlxdWUgbVJOQSBtb2xlY3VsZXMgKFVNSXMpIGNvbXBhcmVkIHRvIGVtcHR5IEdFTXMuIFRoZSBrbmVlIHBsb3QgdHlwaWNhbGx5IHNob3dzIGEgc3VkZGVuIGRyb3AgKGtuZWUpIGluIFVNSXMsIGluZGljYXRpbmcgdGhlIHNlcGFyYXRpb24gYmV0d2VlbiBHRU1zIGNvbnRhaW5pbmcgY2VsbHMgYW5kIHRob3NlIGxhY2tpbmcgdGhlbS4NCg0KWyFbRmlndXJlIGZyb20gRGFuaWVsc2tpLCBLLiAoMjAyMykuIEd1aWRhbmNlIG9uIFByb2Nlc3NpbmcgdGhlIDEweCBHZW5vbWljcyBTaW5nbGUgQ2VsbCBHZW5lIEV4cHJlc3Npb24gQXNzYXkuIEluOiBDYWxvZ2VybywgUi5BLiwgQmVuZXMsIFYuIChlZHMpIFNpbmdsZSBDZWxsIFRyYW5zY3JpcHRvbWljcy4gTWV0aG9kcyBpbiBNb2xlY3VsYXIgQmlvbG9neSwgdm9sIDI1ODQuIEh1bWFuYSwgTmV3IFlvcmssIE5ZLiBodHRwczovL2RvaS5vcmcvMTAuMTAwNy85NzgtMS0wNzE2LTI3NTYtM18xXShpbWFnZXMvY2xpcGJvYXJkLTQyODA2OTk4ODAucG5nKXtzdHlsZT0iYm9yZGVyOjFweCBzb2xpZCAjMDAwMDAwOyBwYWRkaW5nOjNweDsgbWFyZ2luOjJweCIgd2lkdGg9IjY0MiJ9XShodHRwczovL2xpbmsuc3ByaW5nZXIuY29tL3Byb3RvY29sLzEwLjEwMDcvOTc4LTEtMDcxNi0yNzU2LTNfMSkNCg0KSW50ZXJwcmV0aW5nIHRoZSBrbmVlIHBsb3QgaW52b2x2ZXMgaWRlbnRpZnlpbmcgdGhpcyBrbmVlIHBvaW50LiBBIGRpc3RpbmN0IGtuZWUgc3VnZ2VzdHMgc3VjY2Vzc2Z1bCBzZXBhcmF0aW9uLCB3aGlsZSB0aGUgYWJzZW5jZSBvZiBhIGNsZWFyIGtuZWUgY291bGQgaW5kaWNhdGUgaXNzdWVzIHN1Y2ggYXMgbG93IFJOQSBpbnRlZ3JpdHkgb3IgaGlnaCBhbWJpZW50IFJOQSBjb250YW1pbmF0aW9uLg0KDQpbIVtGaWd1cmUgZnJvbSBodHRwczovL2lzb3NlcS5ob3cvdW1pL2NlbGwtY2FsbGluZy5odG1sXShpbWFnZXMvY2xpcGJvYXJkLTU3MjkyMTQ0OC5wbmcpe3N0eWxlPSJib3JkZXI6MXB4IHNvbGlkICMwMDAwMDA7IHBhZGRpbmc6M3B4OyBtYXJnaW46MnB4IiB3aWR0aD0iNjc2In1dKGh0dHBzOi8vaXNvc2VxLmhvdy91bWkvY2VsbC1jYWxsaW5nLmh0bWwpDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLiBTZXR0aW5nIHVwIG91ciBSIGVudmlyb25tZW50IGZvciBkYXRhIGV4cGxvcmF0aW9uDQoNCkluIHByZXBhcmF0aW9uIGZvciB0aGUgZm9sbG93aW5nIGhhbmRzLW9uIGV4cGVyaWVuY2VzIHdlIHdpbGwgc2V0IHVwIGFuIFIgZW52aXJvbm1lbnQgd2hpY2ggaGFzIGV2ZXJ5dGhpbmcgd2UgbmVlZC4NCg0KIyMjICoqMi4xLiBMb2FkIGxpYnJhcmllcyoqDQoNCmBgYHtyfQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsNCiAgbGlicmFyeShTZXVyYXQpDQogIGxpYnJhcnkoZ2dwbG90MikNCiAgbGlicmFyeSh0aWR5cikNCiAgbGlicmFyeShzY0RibEZpbmRlcikNCiAgbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkNCn0pDQpgYGANCg0KIyMjICoqMi4yLiBJbXBvcnQgZGF0YSoqDQoNCkZvciB0aGlzIHR1dG9yaWFsIHdlIHdpbGwgdXNlIHJhdyB1bnByb2Nlc3NlZCBkYXRhIGZyb20gaHVtYW4gcGVyaXBoZXJhbCBibG9vZCBtb25vbnVjbGVhciBjZWxscyAoUEJNQ3MpIGdlbmVyYXRlZCB1c2luZyB0aGUgMTBYIEdlbm9taWNzIHBsYXRmb3JtIHdoaWNoIGNhbiBiZSBkb3dubG9hZCBmcm9tIHRoZSBbU2V1cmF0IHBhZ2VdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvcGJtYzNrX3R1dG9yaWFsKS4gVGhlc2UgZGF0YSBzaG91bGQgYmUgZG93bmxvYWRlZCwgZXh0cmFjdGVkLCBhbmQgcGxhY2VkIGluIHRoZSBkb3dubG9hZHMgZm9sZGVyIGZvciB0aGlzIHdvcmtzaG9wLg0KDQpgYGB7cn0NCiMgTG9hZCB0aGUgUEJNQyBkYXRhc2V0DQpwYm1jLmRhdGEgPC0gUmVhZDEwWChkYXRhLmRpciA9ICIuL2Rvd25sb2Fkcy9maWx0ZXJlZF9nZW5lX2JjX21hdHJpY2VzL2hnMTkvIikNCiMgSW5pdGlhbGl6ZSB0aGUgU2V1cmF0IG9iamVjdCB3aXRoIHRoZSByYXcgKG5vbi1ub3JtYWxpemVkIGRhdGEpLg0Kc2V1cmF0X29iamVjdCA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gcGJtYy5kYXRhLCBwcm9qZWN0ID0gInBibWMzayIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmNlbGxzID0gMywgbWluLmZlYXR1cmVzID0gMjAwKQ0KYGBgDQoNCipOb3RlOiBXZSBhcHBseSBzb21lIHByZWxpbWluYXJ5IGZpbHRlcnMgYXMgd2Uga25vdyB0aGF0IEdFTXMgd2hpY2ggdmVyeSBsb3cgbnVtYmVycyBvZiBnZW5lcyBvciBnZW5lcyB3aGljaCBhcmUgYmFyZWx5IGRldGVjdGVkIGFyZSB1bmxpa2VseSB0byBiZSBhY3R1YWwgY2VsbHMuKg0KDQojIyMgKioyLjMuIENoZWNrIHRoYXQgZXZlcnl0aGluZyB3YXMgbG9hZGVkIGNvcnJlY3RseSoqDQoNCmBgYHtyfQ0KcHJpbnQoc2V1cmF0X29iamVjdCkNCmBgYA0KDQojIyAzLiBPdmVydmlldyBvZiBTaW5nbGUtY2VsbCBEYXRhIFN0cnVjdHVyZQ0KDQpUbyBiZXR0ZXIgdW5kZXJzdGFuZCBob3cgdG8gd29yayB3aXRoIHNpbmdsZS1jZWxsL251Y2xlaSBSTkEgc2VxdWVuY2luZyBkYXRhIHdlIHdpbGwgdGFrZSBhIGRlZXAgZGl2ZSBpbnRvIGhvdyBkYXRhc2V0cyBhcmUgKnR5cGljYWxseSogc3RydWN0dXJlZC4gSW4gdGhlIGRpYWdyYW0gYmVsb3cgd2UgaWxsdXN0cmF0ZSBob3cgZHJvcGxldC1iYXNlZCBzY1JOQXNlcSBhcHByb2FjaGVzIHNlcGFyYXRlIGNlbGxzIGFuZCB0aGVpciBtUk5Bcy4gQnV0IGhvdyBkb2VzIHRoaXMgdHJhbnNsYXRlIGludG8gWyoqKmRhdGEqKipdey51bmRlcmxpbmV9Pw0KDQohW0dlbmVyYXRlZCB1c2luZyBiaW9yZW5kZXIuY29tXShpbWFnZXMvY2xpcGJvYXJkLTI3OTMxNDI3NDMucG5nKXtzdHlsZT0iYm9yZGVyOjFweCBzb2xpZCAjMDAwMDAwOyBwYWRkaW5nOjNweDsgbWFyZ2luOjJweCIgd2lkdGg9IjQwMyJ9DQoNCiMjIyAzLjEuIFRoZSBHZW5lIHggQ2VsbCBNYXRyaXgNCg0KYGBge3J9DQojIFJhdyBjb3VudHMgaW4gYSBzZXVyYXQgb2JqZWN0IGlzIHNhdmVkIGluIHRoZSBhc3NheXMtUk5BLWNvdW50cyBzbG90IA0KcmF3X2NvdW50cyA9IGFzLm1hdHJpeChzZXVyYXRfb2JqZWN0QGFzc2F5cyRSTkFAbGF5ZXJzJGNvdW50cykNCnJvd25hbWVzKHJhd19jb3VudHMpID0gcm93bmFtZXMoc2V1cmF0X29iamVjdCkNCmNvbG5hbWVzKHJhd19jb3VudHMpID0gY29sbmFtZXMoc2V1cmF0X29iamVjdCkNCg0KIyBMZXRzIGxvb2sgYXQgdGhlIGZpcnN0IDQgY29sdW1ucyBhbmQgcm93cw0KcHJpbnQocmF3X2NvdW50c1sxOjQsMTo0XSkNCmBgYA0KDQoqKipXaGF0IGRvZXMgdGhpcyBtZWFuPyoqKg0KDQoqKkNvbHVtbnM6KiogVGhlIGNvbHVtbnMgcmVwcmVzZW50IGEgdW5pcXVlIGBiYXJjb2RlYC4gQXMgd2UgY2FuIHNlZSBiZWxvdywgdGhpcyBgYmFyY29kZWAgbWFya3MgYW4gaW5kaXZpZHVhbCBjZWxsIHdoaWNoIGlzIGhvdyB3ZSBjYW4gaWRlbnRpZnkgZWFjaCBvbmUuIFNvIGluIHRoaXMgb2JqZWN0LCB0aGUgY29sdW1ucyBhcmUgdGhlIGNlbGxzDQoNCiFbR2VuZXJhdGVkIGluIGJpb3JlbmRlci5jb20gYW5kIGFkYXB0ZWQgZnJvbSBEYW5pZWxza2ksIEsuICgyMDIzKS4gR3VpZGFuY2Ugb24gUHJvY2Vzc2luZyB0aGUgMTB4IEdlbm9taWNzIFNpbmdsZSBDZWxsIEdlbmUgRXhwcmVzc2lvbiBBc3NheS4gSW46IENhbG9nZXJvLCBSLkEuLCBCZW5lcywgVi4gKGVkcykgU2luZ2xlIENlbGwgVHJhbnNjcmlwdG9taWNzLiBNZXRob2RzIGluIE1vbGVjdWxhciBCaW9sb2d5LCB2b2wgMjU4NC4gSHVtYW5hLCBOZXcgWW9yaywgTlkuIDxodHRwczovL2RvaS5vcmcvMTAuMTAwNy85NzgtMS0wNzE2LTI3NTYtM18xPl0oaW1hZ2VzL2NsaXBib2FyZC0xNjA2Mjg1NDk1LnBuZyl7c3R5bGU9ImJvcmRlcjoxcHggc29saWQgIzAwMDAwMDsgcGFkZGluZzozcHg7IG1hcmdpbjoycHgifQ0KDQoqKlJvd3M6KiogV2Ugc2VlIHRoYXQgdGhlIGZpcnN0IGZldyByb3dzIGhhdmUgbmFtZXMgc3VjaCBhcyAqUlAxMS0yMDZMMTAuOS4qIEEgcXVpY2sgKmdvb2dsZSogc2VhcmNoIHdpbGwgdGVsbCB5b3UgdGhhdCB0aGlzIGlzIGEgaHVtYW4gZ2VuZSAoRW5zZW1ibCBJRCAtIEVOU0cwMDAwMDIzNzQ5MSkuIFNvIHdlIHNlZSBpbiB0aGlzIG9iamVjdCB0aGF0IHRoZSByb3dzIHJlcHJlc2VudCB0aGUgZ2VuZXMuDQoNCiMjIyAzLjIuIENvdW50aW5nIHRoZSBudW1iZXIgb2YgY2VsbHMgYW5kIGdlbmVzDQoNClNpbmdsZS1jZWxsIHRyYW5zY3JpcHRvbWljIGFuYWx5c2lzIHRvb2xzICgqZS5nLiosIFNldXJhdCkgZG9lcyBhIGxvdCBvZiB0aGUgaGVhdnkgbGlmdGluZyBmb3IgdXMgd2hlbiBpdCBjb21lcyB0byBjb3VudHMgYW5kIG90aGVyIHN1bW1hcnkgZGF0YSBpbmZvcm1hdGlvbi4gRm9yIGV4YW1wbGUsIHRoZSBmb2xsb3dpbmcgY29kZSB3aWxsIHRlbGwgeW91IHRoZSBudW1iZXIgb2YgZ2VuZXMgYW5kIGNlbGxzLCBhcyB3ZWxsIHN0b3JlIHJhdywgbm9ybWFsaXplZCwgYW5kIHNjYWxlZCBkYXRhLCBkaW1lbnNpb24gcmVkdWN0aW9ucywgYW5kIG90aGVyIGFuYWx5c2VzIHdoaWNoIG1heSBoYXZlIGJlZW4gcGVyZm9ybWVkLg0KDQpgYGB7cn0NCnByaW50KHNldXJhdF9vYmplY3QpDQpgYGANCg0KVGhpcyBpbmRpY2F0ZXMgdGhhdCB3ZSBoYXZlIDEzLDcxNCBnZW5lcyAoZmVhdHVyZXMpIGFjcm9zcyAyLDcwMCBzYW1wbGVzLg0KDQpIb3dldmVyLCB3ZSBjYW4gYWxzbyBleHRyYWN0IHRoYXQgaW5mb3JtYXRpb24gZnJvbSB0aGUgY291bnQgdGFibGU6DQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUwKCdDZWxsczogJywgbmNvbChyYXdfY291bnRzKSkpDQpwcmludChwYXN0ZTAoJ0ZlYXR1cmVzOicsIG5yb3cocmF3X2NvdW50cykpKQ0KYGBgDQoNClRoZXNlIG51bWJlcnMgc2hvdWxkIG1hdGNoIQ0KDQojIyMgMy4zLiBBIGRlZXBlciBkaXZlIGludG8gY291bnRzDQoNCiMjIyMgMy4zYS4gV2hhdCBpcyBzZXF1ZW5jaW5nIGRlcHRoPw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCByZWFkcyBpbiBlYWNoIGluZGl2aWR1YWwgY2VsbA0KcmVhZF9kZXB0aHMgPSBjb2xTdW1zKHJhd19jb3VudHMpDQoNCiMgSG93IGFyZSB0aGV5IGRpc3RyaWJ1dGVkPw0KaGlzdChyZWFkX2RlcHRocywgbWFpbiA9ICJIaXN0b2dyYW0gb2YgcmVhZCBkZXB0aCIsIA0KICAgICB4bGFiID0gIkRlcHRoIiwgeWxhYiA9ICJGcmVxdWVuY3kiLA0KICAgICBicmVha3MgPSAyMDApDQpgYGANCg0KVGhpcyBoaXN0b2dyYW0gc2hvd3MgdGhhdCB0aGUgbnVtYmVyIG9mIG1STkEgdHJhbnNjcmlwdHMgZGV0ZWN0ZWQgaW4gZWFjaCBjZWxsIHJhbmdlZCBmcm9tIDU0NiB0byAxNSw4MTgsIGJ1dCB0aGF0IG1vc3Qgd2VyZSBhcm91bmQgMiwwMDAuIFNvIHdoYXQgbWlnaHQgYWNjb3VudCBmb3IgdGhlc2UgZGlmZmVyZW5jZXM/DQoNCi0gICAqKkNlbGwgdHlwZXM6KiogU29tZSBjZWxsIHR5cGVzIG1heSBzaW1wbHkganVzdCBleHByZXNzIG1vcmUgbVJOQSB0aGFuIG90aGVycy4gRm9yIGV4YW1wbGUsIEhlcGF0b2N5dGVzIGFyZSBrbm93biB0byBjb250YWluIGEgbG90IG9mIFJOQSB3aGlsZSBvdGhlcnMgbWF5IG5vdC4NCg0KLSAgICoqU3RvY2hhc3RpYyBnZW5lIGV4cHJlc3Npb246KiogRmx1Y3R1YXRpb25zIGluIHRyYW5zY3JpcHRpb24gYW5kIGRlZ3JhZGF0aW9uIGZyb20gbm9ybWFsIGJpb2xvZ2ljYWwgcHJvY2Vzc2VzLCBjZWxsIHN0YXRlcywgYW5kIG90aGVyIGZhY3RvcnMgaW4gaW5kaXZpZHVhbCBjZWxscyB3aGljaCBoYXZlIHZlcnkgbG93IGFtb3VudHMgb2YgUk5BIHRvIGJlZ2luIGNhbiBhcHBlYXIgYXMgYmlnIGRpZmZlcmVuY2VzLg0KDQotICAgKipUZWNobmljYWwgdmFyaWFiaWxpdHk6KiogU2VxdWVuY2luZyBkZXB0aCwgYXNzYXkga2l0cywgYW5kIG90aGVyIHRlY2huaWNhbCBmYWN0b3JzIG1heSBjb250cmlidXRlIHRvIHRoZXNlIGRpZmZlcmVuY2VzLg0KDQpbKlRoZSB6ZXJvLWluZmxhdGlvbiBxdWVzdGlvbipdey51bmRlcmxpbmV9KjogSW4gdGhlIGVhcmx5IGRheXMgb2Ygc2NSTkFzZXEgaXQgd2FzIGEgd2lkZWx5IGFjY2VwdGVkIHRoYXQgZGF0YSB3YXMgemVyby1pbmZsYXRlZCwgaS5lLiwgaXQgaGFkIG1vcmUgemVybydzIHRoYW4gaXQgc2hvdWxkIGJlY2F1c2Ugb2YgdGVjaG5pY2FsIGNoYWxsZW5nZXMuIFRvZGF5LCB3ZSBiZXR0ZXIgdW5kZXJzdGFuZCB0aGF0IHRoZSBsYXJnZSBudW1iZXIgb2YgemVybydzIGFyZSBub3Qgb25seSB0ZWNobmljYWwsIGJ1dCBpbiBmYWN0IGRvZXMgcmVmbGVjdCB0cnVlIGJpb2xvZ3kgYXMgd2VsbC4gSGVyZSBhcmUgc29tZSBpbnRlcmVzdGluZyBwYXBlcnMgb24gdGhlIHRvcGljOiBbSmlhbmcgZXQgYWwuLCAyMDIyXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDIyLTAyNjAxLTUpKg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNC4gQXNzZXNzaW5nIGRhdGEgcXVhbGl0eQ0KDQpBbnkgYW5hbHlzaXMsIG5vIG1hdHRlciBvZiBzaXplLCBzaG91bGQgdW5kZXJnbyByaWdvcm91cyBRQy4gV2hlbiBpdCBjb21lcyB0byBzaW5nbGUtY2VsbCB0cmFuc2NyaXB0b21pYyBkYXRhLCB0aGVyZSBhcmUgc2V2ZXJhbCBjb21wdXRhdGlvbmFsIHRvb2xzIHRoYXQgaGVscCB5b3UgaW4gdGhlIHByb2Nlc3MgZnJvbSBlbmFibGluZyB2aXN1YWxpemF0aW9ucyB0byBwZXJmb3JtaW5nIGEgcGFuZWwgb2YgUUMgdGVzdHMgYW5kIHJlcG9ydGluZyBzZXZlcmFsIGRpZmZlcmVudCBtZXRyaWNzIGZvciB0aGUgZW5kIHVzZXIgdG8gZXZhbHVhdGUuIEEgbGlzdCBvZiBqdXN0IHNvbWUgb2YgdGhlc2UgdG9vbHMgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3d3dy5zY3JuYS10b29scy5vcmcvdG9vbHM/c29ydD1uYW1lJmNhdHM9UXVhbGl0eUNvbnRyb2wpLg0KDQpgYGAgICAgICAgICANCmh0dHBzOi8vd3d3LnNjcm5hLXRvb2xzLm9yZy8NCmh0dHBzOi8vd3d3LnNjLWJlc3QtcHJhY3RpY2VzLm9yZy9wcmVwcm9jZXNzaW5nX3Zpc3VhbGl6YXRpb24vcXVhbGl0eV9jb250cm9sLmh0bWwNCmBgYA0KDQpMZXQncyBnbyB0aHJvdWdoIHNvbWUgb2YgdGhlIG1vc3QgY29tbW9uIFFDIG1ldHJpY3MsIHdoYXQgdG8gdGhleSBtZWFuLCBhbmQgd2h5IGRvIHRoZXkgbWF0dGVyPw0KDQojIyMgNC4xLiBHZW5lcyBhbmQgdHJhbnNjcmlwdHMNCg0KKkhvdyBkb2VzIHRoZSBudW1iZXIgb2YgZGV0ZWN0ZWQgZ2VuZXMgYW5kIHRyYW5zY3JpcHRzIGluZGljYXRlIHF1YWxpdHk/KiBXaGlsZSB3ZSBkb24ndCBoYXZlIGEga25vd24gbnVtYmVyIG9mIGV4cHJlc3NlZCBnZW5lcyBmb3IgZWFjaCBjZWxsIHR5cGUsIHdlIGRvIGtub3cgdGhhdCBjZWxscyByZXF1aXJlIHRoZSBleHByZXNzaW9uIG9mIGh1bmRyZWRzIHRvIHRob3VzYW5kcyBvZiBnZW5lcyB0byBmdW5jdGlvbiBwcm9wZXJseS4NCg0KKipOdW1iZXIgb2YgZ2VuZXMgKGZlYXR1cmVzKSoqDQoNCldlIGNhbiBjaGVjayB0aGVzZSB2YWx1ZXMgYnkgcnVubmluZyB0aGUgY29kZSBiZWxvdzoNCg0KYGBge3J9DQpWbG5QbG90KHNldXJhdF9vYmplY3QsIGZlYXR1cmVzID0gJ25GZWF0dXJlX1JOQScpICsgTm9MZWdlbmQoKQ0KYGBgDQoNCkhlcmUgd2UgY2FuIHNlZSB0aGF0ICgxKSB0aGUgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkIHJhbmdlcyBmcm9tIFx+MjAwIC0gMzAwMCBhbmQgKDIpIG1vc3QgZXhwcmVzcyBcfjkwMCBnZW5lcy4gKklzIHRoaXMgbm9ybWFsIGFuZCBleHBlY3RlZD8qIEluIHJlYWxpdHksIHRoZXJlIGlzIG5vIHVuaXZlcnNhbCB0aHJlc2hvbGQsIHRoZXNlIHZhbHVlcyB3aWxsIGRlcGVuZCBvbiB0aGUgY2VsbCB0eXBlIGFuZCB0aGUgc2VxdWVuY2luZyBkZXB0aC4gQnV0IHdlIGRvIGtub3cgdGhhdCBpZiBtb3N0IGNlbGxzIGhhZCBleHRlbWVseSBsb3cgY291bnRzIHRoYXQgZWl0aGVyIGNlbGxzIHdlcmUgbm90IGhlYWx0aHkvaW50YWN0IG9yIHNlcXVlbmNpbmcgZGVwdGggd2FzIG11Y2ggdG9vIGxvdy4gSXQgY291bGQgYWxzbyBwb2ludCB0byBlcnJvcnMgZnVydGhlciB1cHN0cmVhbSBzdWNoIGFzIGFsaWdubWVudCB0byB0aGUgaW5jb3JyZWN0IHJlZmVyZW5jZSBnZW5vbWUuDQoNCioqTnVtYmVyIG9mIG1STkEgbW9sZWN1bGVzIChjb3VudCkqKg0KDQpXZSBjYW4gYWxzbyBleGFtaW5lIHRoZSB0b3RhbCBudW1iZXIgb2YgbVJOQSBtb2xlY3VsZXMgZGV0ZWN0ZWQ6DQoNCmBgYHtyfQ0KVmxuUGxvdChzZXVyYXRfb2JqZWN0LCBmZWF0dXJlcyA9ICduQ291bnRfUk5BJykNCmBgYA0KDQpBcyBhYm92ZSwgd2UgY2FuIHNlZSBhIGdvb2QgZGlzdHJpYnV0aW9uIGNlbnRlcmVkIGFyb3VuZCAyNTAwIG1STkEgbW9sZWN1bGVzLiBUaGVzZSB2YWx1ZSBzaG91bGQgYWx3YXlzIGJlIGhpZ2hlciB0aGFuIHRoZSBudW1iZXIgb2YgZmVhdHVyZXMuDQoNCioqQ29ycmVsYXRpb24gb2YgZmVhdHVyZXMgYW5kIG1vbGVjdWxlcyoqDQoNCkEgdGhpcmQgd2F5IHRvIGxvb2sgYXQgdGhpcyBkYXRhIGlzIHRvIGV4YW1pbmUgdGhlIGNvcnJlbGF0aW9uIG9mIGZlYXR1cmVzIGFuZCBtUk5BIG1vbGVjdWxlczoNCg0KYGBge3J9DQpGZWF0dXJlU2NhdHRlcihvYmplY3QgPSBzZXVyYXRfb2JqZWN0LCBmZWF0dXJlMSA9ICduQ291bnRfUk5BJywgZmVhdHVyZTIgPSAnbkZlYXR1cmVfUk5BJykNCmBgYA0KDQpUaGlzIG1ldHJpYyBpcyBtb3JlIGluZm9ybWF0aXZlIGJlY2F1c2UgaXQgdGVsbHMgeW91IHRoYXQgYXMgeW91IGRldGVjdCBtb3JlIG1STkEgbW9sZWN1bGVzLCB5b3UgYWxzbyBkZXRlY3QgbW9yZSBkaWZmZXJlbnQgZ2VuZXMuIFRoaXMgd291bGQgZ2VuZXJhbGx5IGJlIGV4cGVjdGVkIGFzIHlvdSBhcmUgZ2V0dGluZyBhIGxhcmdlciBzYW1wbGluZyBvZiB0aGUgbVJOQSByZXN1bHRpbmcgaW4gYW4gaW5jcmVhc2VkIGNoYW5jZSBvZiBzYW1wbGluZyBkaWZmZXJlbnQgZ2VuZXMuIEhvd2V2ZXIsIHdoZW4geW91IGFzc2VzcyB0aGVzZSBwbG90cywgeW91IHdpbGwgd2FudCB0byBbKiprZWVwIHRoZSBiaW9sb2d5IGluIG1pbmQqKl17LnVuZGVybGluZX0uDQoNCiMjIyA0LjIuIE1pdG9jaG9uZHJpYWwgUk5BDQoNCkhlcmUsIGJpb2xvZ2ljYWwga25vd2xlZGdlIGlzIGV4dHJlbWVseSBpbXBvcnRhbnQgZm9yIGV2YWx1YXRpbmcgdGhpcyBtZXRyaWMgYW5kIHJlbW92aW5nIGNlbGxzIGJhc2VkIG9uIGEgdGhyZXNob2xkLiBDb25zaWRlciB0aGVzZSB0aHJlZSBleHBlcmltZW50czoNCg0KLSAgIE51Y2xlaSBpc29sYXRlZCBmcm9tIG11c2NsZSBjZWxscyB3aGljaCByZXF1aXJlIGEgbG90IG9mIGVuZXJneQ0KDQotICAgTXVzY2xlIHdob2xlIGNlbGxzIHdoaWNoIHJlcXVpcmUgYSBsb3Qgb2YgZW5lcmd5DQoNCi0gICBTa2luIGNlbGxzIHdoaWNoIGFyZSBsZXNzIG1ldGFib2xpY2FsbHkgYWN0aXZlDQoNCipXaGF0IHdvdWxkIHlvdSBleHBlY3QgdG8gc2VlIChxdWFsaXRhdGl2ZSkgaW4gdGhlIGFtb3VudCBvZiBtaXRvY2hvbmRyaWFsIGdlbmVzIGZvciBlYWNoIG9mIHRoZXNlIGV4cGVyaW1lbnRzPyoNCg0KKipBc3Nlc3NpbmcgcGVyY2VudCBtaXRvY2hvbmRyaWEgY291bnRzKioNCg0KV2UgY2FuIGFzc2VzcyB0aGUgcGVyY2VudGFnZSBvZiBtaXRvY2hvbmRyaWFsIGdlbmVzIGJ5IGNhbGN1bGF0aW5nIHRoZSBudW1iZXIgb2YgY291bnRzIGNvbWluZyBmcm9tIG1pdG9jaG9uZHJpYWwgZ2VuZXMgKHVzdWFsbHkgcHJlZml4ZWQgd2l0aCBtdC0gb3IgTVQtKSBjb21wYXJlZCB0byB0aGUgdG90YWwgY291bnRzLiBUaGlzIGNhbiBiZSB2aXN1YWxpemVkIGxpa2UgdGhpczoNCg0KYGBge3J9DQojIENhbGN1YXRlIHRoZSBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgY291bnRzDQpzZXVyYXRfb2JqZWN0W1sicGVyY2VudC5tdCJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChzZXVyYXRfb2JqZWN0LCBwYXR0ZXJuID0gIl5NVC0iKQ0KDQojIFBsb3QgdGhlIHZhbHVlcw0KVmxuUGxvdChzZXVyYXRfb2JqZWN0LCBmZWF0dXJlcyA9ICdwZXJjZW50Lm10JykNCmBgYA0KDQpXZSBzZWUgdGhhdCB0aGUgcGVyY2VudGFnZSBvZiBjb3VudHMgcmVwcmVzZW50aW5nIG1pdG9jaG9uZHJpYWwgZ2VuZXMgaXMgbW9zdGx5IGJlbG93IDUlIGJ1dCBhcyBoaWdoIGFzIFx+MjUlLiBUbyBldmFsdWF0ZSB0aGVzZSByZXN1bHRzIHRoaW5rIGJhY2sgdG8gdGhlIHRocmVlIGV4cGVyaW1lbnRzIGFuZCBjb25zaWRlciB3aGF0IHlvdSB3b3VsZCBleHBlY3QgdG8gc2VlOg0KDQotICAgTnVjbGVpIGlzb2xhdGVkIGZyb20gbXVzY2xlIGNlbGxzIHdoaWNoIHJlcXVpcmUgYSBsb3Qgb2YgZW5lcmd5DQoNCiAgICBEZXNwaXRlIGJlaW5nIG1ldGFib2xpY2FsbHkgYWN0aXZlLCBudWNsZWkgaXNvbGF0aW9uIGlzIGV4cGVjdGVkIHRvIHdhc2ggb3V0IG1pdG9jaG9uZHJpYS4gSGlnaCBjb250YW1pbmF0aW9uIGlzIGluZGljYXRpdmUgb2YgaGlnaCBhbWJpZW50IFJOQSBvciBtaXRvY2hvbmRyaWEgc3RpY2tpbmcgdG8gdGhlIHRoZSBudWNsZWkuDQoNCi0gICBNdXNjbGUgd2hvbGUgY2VsbHMgd2hpY2ggcmVxdWlyZSBhIGxvdCBvZiBlbmVyZ3kNCg0KICAgIEdpdmVuIHRoYXQgbXVzY2xlIGNlbGxzIGFyZSBoaWdobHkgbWV0YWJvbGljYWxseSBhY3RpdmUgYW5kIGFyZSBrbm93biB0byBoYXZlIGFtb25nIHRoZSBtb3N0IG1pdG9jaG9uZHJpYSBhbW9uZyBtYW1tYWxpYW4gY2VsbHMsIHRoZXNlIGRhdGEgbWF5IHNob3cgaGlnaGVyIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCBnZW5lcyB0aGFuIG1vc3QgZGF0YXNldHMuDQoNCi0gICBTa2luIGNlbGxzIHdoaWNoIGFyZSBsZXNzIG1ldGFib2xpY2FsbHkgYWN0aXZlDQoNCiAgICBHaXZlbiB0aGF0IHRoZXNlIGFyZSBsZXNzIG1ldGFib2xpY2FsbHkgYWN0aXZlLCB0aGV5IG1heSBiZSBleHBlY3RlZCB0byBzaG93IHNpbWlsYXIgb3IgbG93ZXIgcGVyY2VudGFnZSBvZiBtaXRvY2hvbmRyaWFsIGdlbmVzLg0KDQoqKkNvcnJlbGF0aW9uIG9mIGNvdW50cyBhbmQgbWl0b2Nob25kcmlhbCBjb3VudHMqKg0KDQpBcyB3aXRoIHRoZSBmZWF0dXJlcyBhbmQgY291bnRzLCB3ZSBjYW4gbG9vayBhdCBob3cgdGhlc2UgY29ycmVsYXRlOg0KDQpgYGB7cn0NCkZlYXR1cmVTY2F0dGVyKHNldXJhdF9vYmplY3QsIGZlYXR1cmUxID0gJ25Db3VudF9STkEnLCBmZWF0dXJlMiA9ICdwZXJjZW50Lm10JykNCmBgYA0KDQpJbnRlcmVzdGluZ2x5LCB0aGVzZSBhcmUgbm90IGNvcnJlbGF0ZWQgYXQgYWxsLiBQcmlvciB0byBmaWx0ZXJpbmcsIHRoZXNlIHZhbHVlcyB3aWxsIGJlIGFudGktY29ycmVsYXRlZCBhcyBtaXRvY2hvbmRyaWEgb2Z0ZW4gbWFrZXMgdXAgdGhlIGFtYmllbnQgUk5BIChyZWxlYXNlIGZyb20gZGVhZCBvciBseXNlZCBjZWxscykgYW5kIHdpbGwgYmUgbW9yZSByZXByZXNlbnRlZCBpbiB0aGUgYmFja2dyb3VuZCAobG93IGNvdW50IGNlbGxzKS4NCg0KIyMjIDQuMy4gQW1iaWVudCBSTkEgY29udGFtaW5hdGlvbg0KDQpBcyB3aXRoIGFueSBzaW5nbGUtY2VsbCB0cmFuc2NyaXB0b21pYyB0b29sLCB0aGVyZSBhcmUgZG96ZW5zLCBpZiBub3QgbW9yZSwgb3B0aW9ucyBmb3IgcmVtb3ZhbCBvZiBhYmllbnQgUk5BLiBHZW5lcmFsbHksIHRoZSBjb25jZXB0IGZvciBhbWJpZW50IFJOQSByZW1vdmFsIGlzIHRoZSBzYW1lIC0gc3Vic3RyYWN0IGdlbmVzIHdoaWNoIGFyZSBwcmVzZW50IGluIHRoZSAqZW1wdHkqIEdFTXMvd2VsbHMgZnJvbSB0aG9zZSB3aGljaCBhcmUgZm91bmQgdG8gaGF2ZSBjZWxscy4gT3RoZXIgdGVjaG5pcXVlcyBzdWNoIGFzIGNvbWJpbmF0b3JpYWwgYmFyY29kaW5nIGFwcHJvYWNoZXMgaGF2ZSB0byBtYWtlIGRpZmZlcmVudCBhc3N1bXB0aW9ucy4NCg0KIVs8aHR0cHM6Ly93d3cuc2MtYmVzdC1wcmFjdGljZXMub3JnL3ByZXByb2Nlc3NpbmdfdmlzdWFsaXphdGlvbi9xdWFsaXR5X2NvbnRyb2wuaHRtbD5dKGltYWdlcy9jbGlwYm9hcmQtMjMyODIwMjQ4My5wbmcpe3dpZHRoPSI2NDMifQ0KDQpbU291cFhdKGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9naWdhc2NpZW5jZS9hcnRpY2xlLzkvMTIvZ2lhYTE1MS82MDQ5ODMxKSBpcyBvbmUgZXhhbXBsZSBvZiBhIHRvb2wgd2hpY2ggY2xlYW5zIHVwIHRoZSAqc291cCogKGZsb2F0aW5nIGFtYmllbnQgUk5BKSBmcm9tIGNlbGxzLiBUaGlzIHRvb2xzIHVzZXMgdGhlIG1vc3QgcmF3IGZvcm1hdCBvZiBkYXRhIHdoaWNoIGlzIGdlbmVyYXRlIGJlZm9yZSBhIHRocmVzaG9sZCBpcyBzZXQgb24gdGhlIGtuZWUgcGxvdCBkaXNjdXNzZWQgYWJvdmUuIEZvciAxMFggR2Vub21pY3MgdGhlc2UgYXJlIHN0b3JlZCBpbiB0aGUgYHJhd19mZWF0dXJlX2JjX21hdHJpeGAgYW5kIGBmaWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeGAgZm9sZGVycy4NCg0KVGhpcyB0dXRvcmlhbCB3aWxsIG5vdCBnbyB0aHJvdWdoIHRoZSBlbnRpcmUgYW1iaWVudCBSTkEgcmVtb3ZhbCB3b3JrZmxvdy4gRXhjZWxsZW50IGV4YW1wbGVzIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9jZWxsZ2VuaS5naXRodWIuaW8vbm90ZWJvb2tzL2h0bWwvbmV3LTEwa1BCTUMtU291cFguaHRtbCkgYW5kIFtoZXJlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvU291cFgvdmlnbmV0dGVzL3BibWNUdXRvcmlhbC5odG1sKS4NCg0KIyMjIDQuNCBEb3VibGV0cw0KDQpBbm90aGVyIGNvbW1vbiBjaGFsbGVuZ2Ugd2l0aCB1c2luZyBzaW5nbGUtY2VsbCB0cmFuc2NyaXB0b21pYyBwbGF0Zm9ybXMgaXMgb25lIHRoYXQgY2FuIGFmZmVjdCBtb3N0IHNpbmdsZS1jZWxsIGJhc2VkIHRlY2huaXF1ZXMgaW5jbHVkaW5nIGZsb3cgY3l0b21ldHJ5IC0gZG91YmxldHMuIFRoZXNlIGNhbiBiZSBhIHJlc3VsdCBvZiBjZWxscyBzdGlja2luZyB0byBlYWNoIG90aGVyIG9yIGJlaW5nIHRvbyBjbG9zZSB0byBlYWNoIG90aGVyIGR1cmluZyB0aGUgKnNvcnRpbmcqLCBhbmQgdGhlIHJhdGUgaXMgdHlwaWNhbGx5IGEgZnVuY3Rpb24gb2YgdGhlIHRvdGFsIG51bWJlciBvZiBjZWxscyBleGFtaW5lZCAobW9yZSBjZWxscyByZXN1bHRzIGluIG1vcmUgZG91YmxldHMpLg0KDQohWzxodHRwczovL3d3dy5zYy1iZXN0LXByYWN0aWNlcy5vcmcvcHJlcHJvY2Vzc2luZ192aXN1YWxpemF0aW9uL3F1YWxpdHlfY29udHJvbC5odG1sPl0oaW1hZ2VzL2NsaXBib2FyZC0zODQzODczNTIucG5nKXt3aWR0aD0iMTEzOSJ9DQoNCkRvdWJsZXRzIGNhbiBjb25zaXN0IG9mIGVpdGhlciAyIGNlbGxzIG9mIHRoZSBzYW1lIHR5cGUgKGhvbW90eXBpYykgb3IgZGlmZmVyZW50IGNlbGwgdHlwZXMgKGhldGVyb3R5cGljKS4gQXMgeW91IGNhbiBwcm9iYWJseSBpbWFnaW5lLCBkaXN0aW5ndWlzaW5nIGhvbW90eXBpYyBkb3VibGV0cyBpcyBtb3JlIGNoYWxsZW5naW5nLCBidXQgYXJlIGdlbmVyYWxseSBjb25zaWRlcmVkIHRvIGJlIGxlc3Mgb2YgYSBjb25jZXJuLg0KDQpbRG91YmxldEZpbmRlcl0oaHR0cHM6Ly93d3cuY2VsbC5jb20vY2VsbC1zeXN0ZW1zL2Z1bGx0ZXh0L1MyNDA1LTQ3MTIoMTkpMzAwNzMtMCkgYW5kIFtzY0RibEZpbmRlcl0oaHR0cHM6Ly93d3cuY2VsbC5jb20vY2VsbC1zeXN0ZW1zL2Z1bGx0ZXh0L1MyNDA1LTQ3MTIoMTkpMzAwNzMtMCkgYXJlIFIgYmFzZWQgdG9vbCBmb3IgdGhlIGlkZW50aWZpY2F0aW9uIG9mIGRvdWJsZXRzIGJhc2VkIG9uIHRoZSBnZW5lcmFsIHByaW5jaXBsZSBvdXRsaW5lZCBhYm92ZS4NCg0KYGBge3J9DQpzY2UgPSBhcy5TaW5nbGVDZWxsRXhwZXJpbWVudChzZXVyYXRfb2JqZWN0KQ0Kc2NlIDwtIHNjRGJsRmluZGVyKHNjZSkNCmBgYA0KDQpgYGB7cn0NCiMgVHJhbnNmZXIgdGhlIGRvdWJsZXQgaW5mb3JtYXRpb24gdG8gdGhlIHNldXJhdCBvYmplY3QNCnNldXJhdF9vYmplY3Qkc2NEYmxGaW5kZXIuY2xhc3MgPSBjb2xEYXRhKHNjZSkkc2NEYmxGaW5kZXIuY2xhc3MNCnNldXJhdF9vYmplY3Qkc2NEYmxGaW5kZXIuc2NvcmUgPSBjb2xEYXRhKHNjZSkkc2NEYmxGaW5kZXIuc2NvcmUNCg0KYGBgDQoNCiMjIyA0LjUuIEZpbHRlcmluZyBkYXRhDQoNCmBgYHtyfQ0Kc2V1cmF0X29iamVjdCA8LSBzdWJzZXQoc2V1cmF0X29iamVjdCwgc3Vic2V0ID0gbkZlYXR1cmVfUk5BID4gMjAwICYgbkZlYXR1cmVfUk5BIDwgMjUwMCAmIHBlcmNlbnQubXQgPCA1ICYgc2NEYmxGaW5kZXIuY2xhc3MgPT0gJ3NpbmdsZXQnKQ0KDQpwcmludChzZXVyYXRfb2JqZWN0KQ0KYGBgDQoNCiMjIDUuIE5vcm1hbGl6YXRpb24NCg0KYGBge3J9DQpzZXVyYXRfb2JqZWN0ID0gU0NUcmFuc2Zvcm0oc2V1cmF0X29iamVjdCwgdmVyYm9zZSA9IEYpDQpzZXVyYXRfb2JqZWN0ID0gUnVuUENBKHNldXJhdF9vYmplY3QsIHZlcmJvc2UgPSBGKQ0Kc2V1cmF0X29iamVjdCA9IFJ1blVNQVAoc2V1cmF0X29iamVjdCwgZGltcyA9IDE6MzAsIHZlcmJvc2UgPSBGKQ0KYGBgDQoNCmBgYHtyfQ0KRGltUGxvdChzZXVyYXRfb2JqZWN0LCBncm91cC5ieT0nb3JpZy5pZGVudCcpDQpgYGANCg0KYGBgICAgICAgICAgDQo2LiBEaW1lbnNpb24gcmVkdWN0aW9uIGFuZCBjbHVzdGVyaW5nOyA3LiBBbm5vdGF0aW9uIG9mIGNlbGwgdHlwZXM7IDguIFRyYWplY3RvcmllcyBhbmQgUk5BIHZlbG9jaXR5OyA5LiBJbmZlcnJpbmcgY2VsbC1jZWxsIGNvbW11bmljYXRpb247IDEwLiBTcGF0aWFsIHRyYW5zY3JpcHRvbWljcw0KYGBgDQo=